package proj2501.net_logo;

import java.awt.event.ActionEvent;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.Random;

import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.Timer;

import proj2501.geo_util.VectorD2;

public class NetLogo {
	Random random = new Random();
	private LinkedList<NLView> views = new LinkedList<NLView>();
	public static final int ORIGIN_CENTER = 0;
	int locationOfOrigin = ORIGIN_CENTER;
	private int minPxcor = -16;
	private int maxPxcor = 16;
	private int minPycor = -16;
	private int maxPycor = 16;
	boolean worldWrapsHorizontally = true;
	boolean worldWrapsVertically = true;
	private double patchSize = 13;
	private double fontSize = 10;
	private double frameRate = 30;
	private int millisDelay = 1000 / 30;
	private boolean tickCounterStarted;
	double ticks;
	public static final int REFRESH_MODE_CONTINUOUS = 0;
	public static final int REFRESH_MODE_ON_TICK = 1;
	private int refreshMode = REFRESH_MODE_CONTINUOUS;
	public void setRefreshMode(int mode) { refreshMode = mode; }
	TickWaitTimer timer;
	Hashtable<VectorD2, NLPatch> patches = new Hashtable<VectorD2, NLPatch>();
	LinkedList<NLTurtle> turtles = new LinkedList<NLTurtle>();
	public LinkedList<NLTurtle> turtles() { return turtles; }
	
	public NetLogo() {
		System.out.println("NetLogo is by Uri Wilensky. http://ccl.northwestern.edu/netlogo/");
		clearAll();
		timer = new TickWaitTimer();
	}
	
	private void initPatches() {
		for (int y = minPycor; y <= maxPycor; y++) {
			for (int x = minPxcor; x <= maxPxcor; x++) {
				VectorD2 v = new VectorD2(x, y);
				NLPatch patch = new NLPatch(v);
//				if (x == 1 && y == 1) {
//					NLColor c = new NLColor("red");
//					patch.setColor(c);
//				}
				patches.put(v, patch);
			}
		}
	}
	
	public JPanel getNewView() {
		NLView panel = new NLView(this);
		views.add(panel);
		return panel;
	}
	
	Hashtable<String, Runnable> commands = new Hashtable<String, Runnable>();
	public void registerCommand(String name, Runnable runnable) {
		Runnable old = commands.get(name);
		if (old != null) {
			throw new RuntimeException("A command named \"" + name + "\" already exists.");
		}
		commands.put(name, runnable);
	}
	
	private LinkedList<NLButton> buttons = new LinkedList<NLButton>();
	public NLButton getNewButton(String commandName, boolean forever, boolean disableUntilTicksStart) {
		NLButton button =  new NLButton(this, commandName, forever, disableUntilTicksStart);
		buttons.add(button);
		return button;
	}
	private void tickWaitDone() {
		if (refreshMode == REFRESH_MODE_ON_TICK) {
			for (NLView view : views) {
				view.repaint();
			}
		}
		invokeAgain();
	}
	
	private void invokeAgain() {
		for (NLButton button : buttons) {
			if (button.forever) {
				button.runCommand();
			}
		}
	}
	
	class TickWaitTimer extends Timer {
		public TickWaitTimer() {
			super(0, new TickWaitDoneAction());
			setRepeats(false);
		}
		@Override
		public void start() {
			setInitialDelay(millisDelay);
			super.start();
		}
	}
	private class TickWaitDoneAction extends AbstractAction {
		@Override
		public void actionPerformed(ActionEvent e) {
			tickWaitDone();
		}
	}
	
	
	/////////////////
	
	public void clearAll() {
		initPatches();
		turtles.clear();
	}
	
	public NLTurtle createTurtle() {
		NLTurtle turtle = new NLTurtle(this);
		turtles.add(turtle);
		return turtle;
	}
	
	public int minPxcor() { return minPxcor; }
	public int minPycor() { return minPycor; }
	public int maxPxcor() { return maxPxcor; }
	public int maxPycor() { return maxPycor; }
	
	public int random(int number) {
		if (0 < number) {
			return random.nextInt(number);
		} else if (number < 0) {
			return - random.nextInt(- number);
		} else {
			return 0;
		}
	}
	
	public double randomFloat(double number) {
		return number * random.nextDouble();
	}
	
	public double randomXcor() {
		return worldWidth() * random.nextDouble() + minPxcor();
	}
	
	public double randomYcor() {
		return worldHeight() * random.nextDouble() + minPycor();
	}
	public void resetTicks() {
		ticks = 0;
		tickCounterStarted = true;
	}
	public void tick() {
		tickAdvance(1);
	}
	public void tickAdvance(double number) {
		ticks += number;
	}
	public double ticks() {
		return ticks;
	}
	public double worldHeight() { return maxPycor() - minPycor() + 1; }
	public double worldWidth() { return maxPxcor() - minPxcor() + 1; }
}
